/**
 *  @版本   animate
 *  @功能   封装匀速动画函数
 *  @param  element      需要做动画的元素
 *  @param  target       目标位置
 *  @param  step         动画步长
 **/
function animate(element, target, step) {
    //* 清空原本的定时器，防止定时器重复启动 */
    clearInterval(element.timer);
    // 启动一个定时器，让一份数据累加，这份数据以后用于做动画效果
    element.timer = setInterval(function () {
        // 获取当前盒子的 水平 坐标值
        var current = element.offsetLeft;
        // 让数据累加
        //* 改变 →: 拿当前位置和目标位置判断大小  */
        // if (current < target) {
        //     current += 9;
        // } else {
        //     current += -9;
        // }
        // 三元表达式：   判断条件 ? 成立执行1 : 不成立执行2
        /* 把步长定义成变量，定义默认值为 40 */
        step = step || 40;
        /* 用三元表达式化简代码 */
        // current < target ? current += 9 : current += -9;
        current += current < target ? step : -step;
        // 把数据应用到盒子上，设置给 left 属性，让盒子动起来
        element.style.left = current + 'px';
        //* 判断是否需要停下来 */
        // 如果目标位置 - 当前位置的值小于 步长，那么就需要停下来
        //* 用绝对值函数取正数 */
        if (Math.abs(target - current) < step) {
            // 清除定时器停止运动
            clearInterval(element.timer);
            console.log('动画停止了');
            // 一瞬间把它设置到目标位置，防止无法到达目标位置
            element.style.left = target + 'px';
        }
    }, 20);
}

/**
 *  @版本  animate_v1
 *  @功能  减速动画函数，变位置
 *  @参数  element      需要做动画的元素
 *  @参数  target       目标位置
 *
 **/
function animate_v1(element, target) {
    //* 清空原本的定时器，防止定时器重复启动 */
    clearInterval(element.timer);
    // 启动一个定时器，让一份数据累加，这份数据以后用于做动画效果
    element.timer = setInterval(function () {
        // 获取盒子的位置
        var current = element.offsetLeft;
        // 减速动画的步长应该是  先大后小
        /* 改变1 →: 步长由  (目标位置-当前位置) / 10 的差值决定，自带方向 */
        var step = (target - current) / 10;

        /* 改变2 →: 为了防止出现小数，把移动数据取整，防止无法到达目标位置的 bug  */
        if (step > 0) {
            step = Math.ceil(step);     // 正方向，向上取整，最少移动 1px
        } else {
            step = Math.floor(step);    // 反方向，向下取整，最少移动 -1px
        }

        // step = (target - current) / 10  已经自带方向了，步长数据直接用就可以了
        current += step;

        // 盒子移动
        element.style.left = current + 'px';

        /* 改变3 →: 因为减速动画后面移动的步长是 1像素的移动，所以肯定能等于目标位置 */
        if (current == target) {
            clearInterval(element.timer);
            // element.style.left = target + 'px';
        }

        /* 最后输出测试的数据 */
        // console.log('目标位置', target, '当前位置', current, '当前移动步长', step);

    }, 20);
}

/**
 *  @版本  animate_v2
 *  @功能  减速动画函数，变宽度
 *  @参数  element      需要做动画的元素
 *  @参数  target       目标位置
 **/
function animate_v2(element, target) {
    //* 清空原本的定时器，防止定时器重复启动 */
    clearInterval(element.timer);
    // 启动一个定时器，让一份数据累加，这份数据以后用于做动画效果
    element.timer = setInterval(function () {
        // 获取盒子的位置
        var current = element.offsetWidth;
        // 减速动画的步长应该是  先大后小
        /* 改变1 →: 步长由  (目标位置-当前位置) / 10 的差值决定，自带方向 */
        var step = (target - current) / 10;

        /* 改变2 →: 为了防止出现小数，把移动数据取整，防止无法到达目标位置的 bug  */
        if (step > 0) {
            step = Math.ceil(step);     // 正方向，向上取整，最少移动 1px
        } else {
            step = Math.floor(step);    // 反方向，向下取整，最少移动 -1px
        }

        // step = (target - current) / 10  已经自带方向了，步长数据直接用就可以了
        current += step;

        // 盒子移动
        element.style.width = current + 'px';

        /* 改变3 →: 因为减速动画后面移动的步长是 1像素的移动，所以肯定能等于目标位置 */
        if (current == target) {
            clearInterval(element.timer);
            // element.style.left = target + 'px';
        }

        /* 最后输出测试的数据 */
        // console.log('目标位置', target, '当前位置', current, '当前移动步长', step);

    }, 20);
}

/**
 *  @版本  animate_v3
 *  @功能  任意属性都可以做动画
 *  @参数  element      需要做动画的元素
 *  @参数  attr        需要做动画的属性
 *  @参数  target      目标位置
 **/
function animate_v3(element, attr, target) {
    //* 清空原本的定时器，防止定时器重复启动 */
    clearInterval(element.timer);
    // 启动一个定时器，让一份数据累加，这份数据以后用于做动画效果
    element.timer = setInterval(function () {
        // 获取盒子的位置
        // var current = element.offsetWidth;
        var current = parseFloat(window.getComputedStyle(element)[attr]);
        // 减速动画的步长应该是  先大后小
        /* 改变1 →: 步长由  (目标位置-当前位置) / 10 的差值决定，自带方向 */
        var step = (target - current) / 10;

        /* 改变2 →: 为了防止出现小数，把移动数据取整，防止无法到达目标位置的 bug  */
        if (step > 0) {
            step = Math.ceil(step);     // 正方向，向上取整，最少移动 1px
        } else {
            step = Math.floor(step);    // 反方向，向下取整，最少移动 -1px
        }

        // step = (target - current) / 10  已经自带方向了，步长数据直接用就可以了
        current += step;

        // 盒子移动
        element.style[attr] = current + 'px';

        /* 改变3 →: 因为减速动画后面移动的步长是 1像素的移动，所以肯定能等于目标位置 */
        if (current == target) {
            clearInterval(element.timer);
            // element.style.left = target + 'px';
        }

        /* 最后输出测试的数据 */
        // console.log('目标位置', target, '当前位置', current, '当前移动步长', step);

    }, 20);
}

/**
 *  @版本   animate_v4
 *  @功能   多个属性都可以做动画
 *  @参数   element          需要做动画的元素
 *  @参数   styleObj         需要做动画的属性
 **/
/* 改变4.1 →: 第二个参数变成了对象  */
function animate_v4(element, styleObj) {
    //* 清空原本的定时器，防止定时器重复启动 */
    clearInterval(element.timer);
    // 启动一个定时器，让一份数据累加，这份数据以后用于做动画效果
    element.timer = setInterval(function () {
        /* 改变4.2 →: for in 遍历所有的样式对象 */
        for (var attr in styleObj) {
            /* 改变4.3 →: 基于对象获取属性的目标值  */
            var target = styleObj[attr];
            // 获取盒子的位置
            //* 改变1 →: 获取当前元素任意样式，并转换成数值型  */
            var current = parseFloat(window.getComputedStyle(element)[attr]);
            // 减速动画的步长应该是  先大后小
            //* 改变1 →: 步长由  (目标位置-当前位置) / 10 的差值决定，自带方向 */
            var step = (target - current) / 10;

            //* 改变2 →: 为了防止出现小数，把移动数据取整，防止无法到达目标位置的 bug  */
            if (step > 0) {
                step = Math.ceil(step);     // 正方向，向上取整，最少移动 1px
            } else {
                step = Math.floor(step);    // 反方向，向下取整，最少移动 -1px
            }
            current += step;
            // 盒子移动
            //* 改变2 →: 根据传进来的属性，修改对应属性的样式  */
            element.style[attr] = current + 'px';
            //* 改变3 →: 因为减速动画后面移动的步长是 1像素的移动，所以肯定能等于目标位置 */
            if (current == target) {
                clearInterval(element.timer);
            }
        }
        /* 改变4.2: for in 循环到这里结束 */

    }, 20);
}

/**
 *  @版本   animate_v5
 *  @功能   多个属性都可以做动画，多个属性都能到达目标位置
 *  @param  element          需要做动画的元素
 *  @param  styleObj         需要做动画的属性
 **/
//* 改变1 →: 第二个参数变成了对象  */
function animate_v5(element, styleObj) {
    //* 清空原本的定时器，防止定时器重复启动 */
    clearInterval(element.timer);
    // 启动一个定时器，让一份数据累加，这份数据以后用于做动画效果
    element.timer = setInterval(function () {
        //* 改变2 →: for in 遍历所有的样式对象 */
        /* 改变5.1 →: 立 flag，假设为 真  */
        var flag = true;
        for (var attr in styleObj) {
            //* 改变3 →: 基于对象获取属性的目标值  */
            var target = styleObj[attr];
            // 获取盒子的位置
            //* 改变1 →: 获取当前元素任意样式，并转换成数值型  */
            var current = parseFloat(window.getComputedStyle(element)[attr]);
            // 减速动画的步长应该是  先大后小
            //* 改变1 →: 步长由  (目标位置-当前位置) / 10 的差值决定，自带方向 */
            var step = (target - current) / 10;

            //* 改变2 →: 为了防止出现小数，把移动数据取整，防止无法到达目标位置的 bug  */
            if (step > 0) {
                step = Math.ceil(step);     // 正方向，向上取整，最少移动 1px
            } else {
                step = Math.floor(step);    // 反方向，向下取整，最少移动 -1px
            }
            current += step;
            // 盒子移动
            //* 改变2 →: 根据传进来的属性，修改对应属性的样式  */
            element.style[attr] = current + 'px';
            //* 改变3 →: 因为减速动画后面移动的步长是 1像素的移动，所以肯定能等于目标位置 */
            /* 改变5.2 →: 没到达目标位置的时候，flag 改成假，不成立，不需要清除定时器  */
            if (current != target) {
                //  如果没到达，立刻改成 假
                flag = false;
                // clearInterval(element.timer);
            }
        }
        /* 改变5.3 →: 全部的属性都到达了目标位置的时候才需要停止定时器  */
        if( flag == true){
            clearInterval(element.timer);
        }
        //* 改变2: for in 循环到这里结束 */

    }, 20);
}

/**
 *  @版本   animate_v6
 *  @功能   透明度能做动画
 *  @param  element          需要做动画的元素
 *  @param  styleObj         需要做动画的属性
 **/
//* 改变1 →: 第二个参数变成了对象  */
function animate_v6(element, styleObj) {
    //* 清空原本的定时器，防止定时器重复启动 */
    clearInterval(element.timer);
    // 启动一个定时器，让一份数据累加，这份数据以后用于做动画效果
    element.timer = setInterval(function () {
        //* 改变2 →: for in 遍历所有的样式对象 */
        //* 改变5.1 →: 立 flag，假设为 真  */
        var flag = true;
        for (var attr in styleObj) {
            //* 改变3 →: 基于对象获取属性的目标值  */
            /* 改变 →: 把目标位置数据放大 100   */
            var target = 100 * styleObj[attr];
            // 获取盒子的位置
            //* 改变1 →: 获取当前元素任意样式，并转换成数值型  */
            /* 改变 →: 把当前位置数据放大 100   */
            var current = 100 * parseFloat(window.getComputedStyle(element)[attr]);
            // 减速动画的步长应该是  先大后小
            //* 改变1 →: 步长由  (目标位置-当前位置) / 10 的差值决定，自带方向 */
            var step = (target - current) / 10;

            //* 改变2 →: 为了防止出现小数，把移动数据取整，防止无法到达目标位置的 bug  */
            if (step > 0) {
                step = Math.ceil(step);     // 正方向，向上取整，最少移动 1px
            } else {
                step = Math.floor(step);    // 反方向，向下取整，最少移动 -1px
            }
            current += step;
            // 盒子移动
            //* 改变2 →: 根据传进来的属性，修改对应属性的样式  */
            /* 改变 →: 在使用的时候，把之前放大的数据缩小回来，并且不能带单位  */
            element.style[attr] = current / 100;
            //* 改变3 →: 因为减速动画后面移动的步长是 1像素的移动，所以肯定能等于目标位置 */
            //* 改变5.2 →: 没到达目标位置的时候，flag 改成假，不成立，不需要清除定时器  */
            if (current != target) {
                //  如果没到达，立刻改成 假
                flag = false;
                // clearInterval(element.timer);
            }
        }
        //* 改变5.3 →: 全部的属性都到达了目标位置的时候才需要停止定时器  */
        if (flag == true) {
            clearInterval(element.timer);
        }
        //* 改变2: for in 循环到这里结束 */

    }, 20);
}

/**
 *  @版本   animate_v7
 *  @功能   层级不需要做动画，透明度能做动画,px的属性能做动画
 *  @param  element          需要做动画的元素
 *  @param  styleObj         需要做动画的属性
 **/
//* 改变1 →: 第二个参数变成了对象  */
function animate_v7(element, styleObj) {
    //* 清空原本的定时器，防止定时器重复启动 */
    clearInterval(element.timer);
    // 启动一个定时器，让一份数据累加，这份数据以后用于做动画效果
    element.timer = setInterval(function () {
        //* 改变2 →: for in 遍历所有的样式对象 */
        //* 改变5.1 →: 立 flag，假设为 真  */
        var flag = true;
        for (var attr in styleObj) {
            //* 改变3 →: 基于对象获取属性的目标值  */
            //* 改变6.1 →: 把目标位置数据放大 100   */
            /* 改变7.1 →: 如果层级属性不需要做动画，怎么办，加判断  */
            if( attr == 'zIndex' ){
                /* 改变7.2 →: 获取层级属性的目标位置  */
                var target = styleObj[attr];
                /* 改变7.3 →: 不需要做动画的属性直接赋值设置就可以了  */
                element.style[attr] = target;
            }else if (attr == 'opacity') {
                var target = 100 * styleObj[attr];
                // 获取盒子的位置
                //* 改变1 →: 获取当前元素任意样式，并转换成数值型  */
                //* 改变6.2 →: 把当前位置数据放大 100   */
                var current = 100 * parseFloat(window.getComputedStyle(element)[attr]);
                // 减速动画的步长应该是  先大后小
                //* 改变1 →: 步长由  (目标位置-当前位置) / 10 的差值决定，自带方向 */
                var step = (target - current) / 10;
                //* 改变2 →: 为了防止出现小数，把移动数据取整，防止无法到达目标位置的 bug  */
                step = step > 0 ? Math.ceil(step) : Math.floor(step);
                current += step;
                // 盒子移动
                //* 改变2 →: 根据传进来的属性，修改对应属性的样式  */
                //* 改变6.3 →: 在使用的时候，把之前放大的数据缩小回来，并且不能带单位  */
                element.style[attr] = current / 100;
                //* 改变3 →: 因为减速动画后面移动的步长是 1像素的移动，所以肯定能等于目标位置 */
                //* 改变5.2 →: 没到达目标位置的时候，flag 改成假，不成立，不需要清除定时器  */
                if (current != target) {
                    //  如果没到达，立刻改成 假
                    flag = false;
                    // clearInterval(element.timer);
                }
            } else {
                //* 改变3 →: 基于对象获取属性的目标值  */
                var target = styleObj[attr];
                // 获取盒子的位置
                //* 改变1 →: 获取当前元素任意样式，并转换成数值型  */
                var current = parseFloat(window.getComputedStyle(element)[attr]);
                // 减速动画的步长应该是  先大后小
                //* 改变1 →: 步长由  (目标位置-当前位置) / 10 的差值决定，自带方向 */
                var step = (target - current) / 10;
                //* 改变2 →: 为了防止出现小数，把移动数据取整，防止无法到达目标位置的 bug  */
                step = step > 0 ? Math.ceil(step) : Math.floor(step);
                current += step;
                // 盒子移动
                //* 改变2 →: 根据传进来的属性，修改对应属性的样式  */
                element.style[attr] = current + 'px';
                //* 改变3 →: 因为减速动画后面移动的步长是 1像素的移动，所以肯定能等于目标位置 */
                /* 改变5.2 →: 没到达目标位置的时候，flag 改成假，不成立，不需要清除定时器  */
                if (current != target) {
                    //  如果没到达，立刻改成 假
                    flag = false;
                    // clearInterval(element.timer);
                }
            }
        }
        //* 改变5.3 →: 全部的属性都到达了目标位置的时候才需要停止定时器  */
        if (flag == true) {
            clearInterval(element.timer);
        }
        //* 改变2: for in 循环到这里结束 */

    }, 20);
}





function animate_v8(element, styleObj, callback) {
    //* 清空原本的定时器，防止定时器重复启动 */
    clearInterval(element.timer);
    // 启动一个定时器，让一份数据累加，这份数据以后用于做动画效果
    element.timer = setInterval(function () {
        //* 改变2 →: for in 遍历所有的样式对象 */
        //* 改变5.1 →: 立 flag，假设为 真  */
        var flag = true;
        for (var attr in styleObj) {
            //* 改变3 →: 基于对象获取属性的目标值  */
            //* 改变6.1 →: 把目标位置数据放大 100   */
            /* 改变7.1 →: 层级属性不需要做动画，怎么办  */
            if( attr == 'zIndex' ){
                /* 改变7.2 →: 获取层级属性的目标位置  */
                var target = styleObj[attr];
                /* 改变7.3 →: 不需要做动画的属性直接赋值设置就可以了  */
                element.style[attr] = target;
            }else if (attr == 'opacity') {
                var target = 100 * styleObj[attr];
                // 获取盒子的位置
                //* 改变1 →: 获取当前元素任意样式，并转换成数值型  */
                //* 改变6.2 →: 把当前位置数据放大 100   */
                var current = 100 * parseFloat(window.getComputedStyle(element)[attr]);
                // 减速动画的步长应该是  先大后小
                //* 改变1 →: 步长由  (目标位置-当前位置) / 10 的差值决定，自带方向 */
                var step = (target - current) / 10;

                //* 改变2 →: 为了防止出现小数，把移动数据取整，防止无法到达目标位置的 bug  */
                step = step > 0 ? Math.ceil(step) : Math.floor(step);

                current += step;
                // 盒子移动
                //* 改变2 →: 根据传进来的属性，修改对应属性的样式  */
                //* 改变6.3 →: 在使用的时候，把之前放大的数据缩小回来，并且不能带单位  */
                element.style[attr] = current / 100;
                //* 改变3 →: 因为减速动画后面移动的步长是 1像素的移动，所以肯定能等于目标位置 */
                //* 改变5.2 →: 没到达目标位置的时候，flag 改成假，不成立，不需要清除定时器  */
                if (current != target) {
                    //  如果没到达，立刻改成 假
                    flag = false;
                    // clearInterval(element.timer);
                }
            } else {
                //* 改变3 →: 基于对象获取属性的目标值  */
                var target = styleObj[attr];
                // 获取盒子的位置
                //* 改变1 →: 获取当前元素任意样式，并转换成数值型  */
                var current = parseFloat(window.getComputedStyle(element)[attr]);
                // 减速动画的步长应该是  先大后小
                //* 改变1 →: 步长由  (目标位置-当前位置) / 10 的差值决定，自带方向 */
                var step = (target - current) / 10;

                //* 改变2 →: 为了防止出现小数，把移动数据取整，防止无法到达目标位置的 bug  */
                step = step > 0 ? Math.ceil(step) : Math.floor(step);

                current += step;
                // 盒子移动
                //* 改变2 →: 根据传进来的属性，修改对应属性的样式  */
                element.style[attr] = current + 'px';
                //* 改变3 →: 因为减速动画后面移动的步长是 1像素的移动，所以肯定能等于目标位置 */
                /* 改变5.2 →: 没到达目标位置的时候，flag 改成假，不成立，不需要清除定时器  */
                if (current != target) {
                    //  如果没到达，立刻改成 假
                    flag = false;
                    // clearInterval(element.timer);
                }

            }


        }
        //* 改变5.3 →: 全部的属性都到达了目标位置的时候才需要停止定时器  */
        if (flag == true) {
            clearInterval(element.timer);
            // console.log('动画结束了');
            /* 改变7.2 判断第三个参数的类型是否为函数 */
            // console.log(typeof callback);
            if( typeof callback == 'function' ){
                /* 改变7.3 →: callback 接收的是一个函数，动画结束后，就调用这个函数  */
                callback();
            }
        }
        //* 改变2: for in 循环到这里结束 */

    }, 20);
}